package percolator

import (
	"distributed-cache/constant"
	"sync"
)

type element struct {
	key  string
	pre  *element
	next *element
}
type CacheQueue struct {
	len   int
	head  *element
	tail  *element
	table map[string]string
}

var m sync.RWMutex

func (cacheQueue *CacheQueue) Add(key string, value string) {
	_, has := cacheQueue.table[key]
	if has {
		cacheQueue.UpdateValue(key, value)
		cacheQueue.Update(key)
		return
	}
	cacheQueue.table[key] = value
	e := &element{key: key}
	m.Lock()
	defer m.Unlock()
	if cacheQueue.head == nil {
		cacheQueue.head = e
		cacheQueue.tail = e
		cacheQueue.len++
		return
	}
	if cacheQueue.len > constant.QUEUELENGTH {
		// 移除尾节点
		preNode := cacheQueue.tail.pre
		preNode.next = nil
		cacheQueue.tail = preNode
		if cacheQueue.table != nil {
			delete(cacheQueue.table, key)
		}
		cacheQueue.len--
	}
	e.next = cacheQueue.head
	cacheQueue.head.pre = e
	cacheQueue.head = e
	cacheQueue.len++

}

func (cacheQueue *CacheQueue) Update(key string) {
	if cacheQueue.head == nil || cacheQueue.len == 1 {
		return
	}
	m.Lock()
	defer m.Unlock()
	node := cacheQueue.head
	for ; node != nil && node.key != key; node = node.next {
	}
	if node == nil || node.pre == nil {
		return
	}
	if node.next == nil {
		cacheQueue.tail = node.pre
		node.pre.next = nil
		node.pre = nil
		cacheQueue.head.pre = node
		node.next = cacheQueue.head
		cacheQueue.head = node
		return
	}

	node.pre.next = node.next
	node.next.pre = node.pre
	cacheQueue.head.pre = node
	node.next = cacheQueue.head
	node.pre = nil
	cacheQueue.head = node
}

func (cacheQueue *CacheQueue) UpdateValue(key string, value string) {
	if cacheQueue.table == nil {
		return
	}
	_, ok := cacheQueue.table[key]
	if !ok {
		return
	}
	cacheQueue.table[key] = value
}

func GetCacheQueue() *CacheQueue {
	table := make(map[string]string, 20)
	return &CacheQueue{len: 0, table: table}
}

func (cacheQueue *CacheQueue) GetCache(key string) (string, bool) {
	if cacheQueue.table == nil {
		return "", false
	}
	value, ok := cacheQueue.table[key]
	if !ok {
		return "", false
	}
	go cacheQueue.Update(key)
	return value, ok
}
